home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Developer Essentials / DTS Sample Code / System 7.0 Samples / ProcDoggie 1.0a6⁄THINK P / UEmergMem.p < prev    next >
Encoding:
Text File  |  1991-02-21  |  17.0 KB  |  452 lines  |  [TEXT/PJMM]

  1. unit UEmergMem;
  2.  
  3. {-------------------------------------------------------------------------------}
  4. {#}
  5. {#    Apple Macintosh Developer Technical Support}
  6. {#}
  7. {#    Interfaces for the emergency memory routines}
  8. {#}
  9. {#    Program:    ProcDoggie}
  10. {#    File:        UEmergMem.p - Pascal Implementation}
  11. {#}
  12. {#    by:        Forrest Tanaka}
  13. {#}
  14. {#    Copyright © 1988-1991 Apple Computer, Inc.}
  15. {#    All rights reserved.}
  16. {#}
  17. {--------------------------------------------------------------------------------}
  18. {#}
  19. {# EmergMem contains routines to handle emergency memory situations.  This is}
  20. {# used for Toolbox routines that either don’t check for memory-full errors, or}
  21. {# that call _SysErr when they can’t allocate the memory that they need.  The}
  22. {# purpose of the routines in this unit is to make sure that these toolbox}
  23. {# routines always get the memory they need.}
  24. {#}
  25. {-------------------------------------------------------------------------------}
  26. {[j=20/57/1$] Pasmat Options}
  27.  
  28. {-------------------------------------------------------------------------------}
  29. {#}
  30. {#     2/21/91 pvh - THINK Pascal conversion.}
  31. {#    Notes:}
  32. {#}
  33. {#}
  34. {-------------------------------------------------------------------------------}
  35.  
  36. interface
  37.  
  38.  
  39. (*******************************************************************************}
  40. {* Used Units}
  41. {*******************************************************************************)
  42.  
  43.     uses
  44.         (* Group 1 *)
  45.         Types, QuickDraw, 
  46.  
  47.         (* Group 2 *)
  48.         Memory, OSUtils;
  49.  
  50.  
  51. (*******************************************************************************}
  52. {* Constants}
  53. {*******************************************************************************)
  54.  
  55.     const
  56.         kAllocApp = TRUE; {For NewPtrMargin/NewHandleMargin for app heap alloc}
  57.         kAllocClr = TRUE; {For NewPtrMargin/NewHandleMargin to clear mem block}
  58.  
  59.  
  60. (*******************************************************************************}
  61. {* ConnectAppGZ - Connect the application grow zone proc}
  62. {*}
  63. {* This routine is called whenever this application’s simple grow-zone procedure}
  64. {* (see UEmergMem.inc1.p for the source for the grow-zone procedure) is to be}
  65. {* connected.  From this point on, any requests for memory by this application or}
  66. {* the system invoke our grow-zone procedure if there isn’t enough memory to}
  67. {* satisfy the request.}
  68. {*******************************************************************************)
  69.  
  70.     procedure ConnectAppGZ;
  71.  
  72.  
  73. (*******************************************************************************}
  74. {* DisconnectAppGZ - Disconnect the application grow zone proc}
  75. {*}
  76. {* This routine is called whenever this application’s simple grow-zone procedure}
  77. {* (see EmergMem.inc1.p for the source for the grow-zone procedure) is to be}
  78. {* disconnected.  From this point on, any requests for memory by this application}
  79. {* or the system return memFullErr if there isn’t enough memory to satisfy the}
  80. {* request.}
  81. {*******************************************************************************)
  82.  
  83.     procedure DisconnectAppGZ;
  84.  
  85.  
  86. (*******************************************************************************}
  87. {* InitEmergMem - Allocate emergency memory}
  88. {*}
  89. {* This is called at startup time to allocate the emergency memory block that’s}
  90. {* deallocated in the grow zone procedure (this application’s grow-zone procedure}
  91. {* is a privately-declared procedure defined in UEmergMem.inc1.p).  InitEmergMem}
  92. {* also installs this application’s grow-zone proc.}
  93. {*}
  94. {* If there isn’t enough memory to allocate the block of emergency memory, then}
  95. {* a subsequent call to FailLowMemory(0) returns TRUE.}
  96. {*******************************************************************************)
  97.  
  98.     procedure InitEmergMem;
  99.  
  100.  
  101. (*******************************************************************************}
  102. {* NoEmergMem - Check to see if emergency memory is being used or not}
  103. {*}
  104. {* Before my application attempts to use more memory, I call this routine to}
  105. {* check if I'm already using my emergency memory.  If so, then I’d better}
  106. {* prepare to die or get my emergency memory back.}
  107. {*******************************************************************************)
  108.  
  109.     function NoEmergMem: Boolean;
  110.  
  111.  
  112. (*******************************************************************************}
  113. {* RecoverEmergMem - Recover emergency memory}
  114. {*}
  115. {* This is called from the event loop if NoEmergMem indicates that the emergency}
  116. {* memory was deallocated by this application’s grow-zone procedure.  This}
  117. {* routine will attempt recover the emergency memory.  If this fails, then some}
  118. {* usually some application options and commands are disabled until there is}
  119. {* enough free memory to enable them again.}
  120. {*******************************************************************************)
  121.  
  122.     procedure RecoverEmergMem;
  123.  
  124.  
  125. (*******************************************************************************}
  126. {* FailLowMemory - Is there enough free space in heap to allocate memory?}
  127. {*}
  128. {* FailLowMemory is called any time a potentially significant amount of non-}
  129. {* temporary memory is about to be allocated.  It returns TRUE if there’s enough}
  130. {* free space in the heap to allocate the requested amount of memory and still}
  131. {* have a significant amount of free space left over, and if the emergency memory}
  132. {* isn’t being used.  See UEmergMem.inc1.p for the definition of “significant}
  133. {* amount.”  "memRequest" specifies the number of bytes that are about to be}
  134. {* allocated.}
  135. {*}
  136. {* This routine is also used even if the amount of memory about to be allocated}
  137. {* isn’t clear.  In this case, it’s called after the significant amount of memory}
  138. {* is allocated and 0 is passed in memRequest.  If FailLowMemory returns TRUE,}
  139. {* then there’s was enough memory for the requested amount and still leave 32K}
  140. {* free and the emergency memory allocated.  If FailLowMemory returns FALSE, then}
  141. {* either there isn’t 32K free, or the emergency memory was deallocated by this}
  142. {* application’s grow-zone procedure, or both.  This is actually the usual way}
  143. {* that I use this function, because I normally use it for calls to the Toolbox,}
  144. {* and there’s usually no reliable way to determine how much memory the Toolbox}
  145. {* is going to allocate.}
  146. {*******************************************************************************)
  147.  
  148.     function FailLowMemory (memRequest: LongInt): Boolean;
  149.  
  150.  
  151.  
  152. (*******************************************************************************}
  153. {* NewHandleMargin - Create a new handle without using emergency memory}
  154. {*}
  155. {* Many toolbox routines simply call SysErr when they run out of memory.  That’s}
  156. {* not too cool, so I try to make certain that the memory they need is always}
  157. {* available by making sure that I never request so much memory that the toolbox}
  158. {* routines are in danger of running out of memory and calling SysErr.  This is}
  159. {* achieved by calling NewHandleMargin instead of NewHandle any time a}
  160. {* relocatable memory block is desired.  NewHandle returns memFullErr in MemErr}
  161. {* if there isn’t enough free contiguous space to satisfy the request and still}
  162. {* leave a significant amount of free memory.}
  163. {*}
  164. {* NewHandleMargin returns NIL if there isn’t enough memory to allocated a block}
  165. {* of the size specified by "requestedSize".}
  166. {*}
  167. {* If "appHeapAlloc" is kAppHeap, then the block of memory is allocated in the}
  168. {* application’s heap.  If "appHeapAlloc" is NOT kAppHeap, then the block of}
  169. {* memory is allocated in the system heap.}
  170. {*}
  171. {* If "clearMem" is kAllocClr, then all the bytes in the block of memory are}
  172. {* cleared to zero.  If NOT kAllocClr is passed, then none of the bytes in the}
  173. {* block of memory are touched after being allocated.}
  174. {*******************************************************************************)
  175.  
  176.     function NewHandleMargin (requestedSize: Size; appHeapAlloc: Boolean; clearMem: Boolean): Handle;
  177.  
  178.  
  179. (*******************************************************************************}
  180. {* NewPtrMargin - Create a new pointer without using emergency memory}
  181. {*}
  182. {* Many toolbox routines simply call SysErr when they run out of memory.  That’s}
  183. {* not too cool, so I try to make certain that the memory they need is always}
  184. {* available by making sure that I never request so much memory that the toolbox}
  185. {* routines are in danger of running out of memory and calling SysErr.  This is}
  186. {* achieved by calling NewPtr instead of NewHandle any time a non-relocatable}
  187. {* memory block is desired.  NewHandle returns memFullErr in MemErr if there}
  188. {* isn’t enough free contiguous space to satisfy the request and still leave a}
  189. {* significant amount of free memory.}
  190. {*}
  191. {* NewptrMargin returns NIL if there isn’t enough memory to allocated a block of}
  192. {* the size specified by "requestedSize".}
  193. {*}
  194. {* If "appHeapAlloc" is kAppHeap, then the block of memory is allocated in the}
  195. {* application’s heap.  If "appHeapAlloc" is NOT kAppHeap, then the block of}
  196. {* memory is allocated in the system heap.}
  197. {*}
  198. {* If "clearMem" is kAllocClr, then all the bytes in the block of memory are}
  199. {* cleared to zero.  If NOT kAllocClr is passed, then none of the bytes in the}
  200. {* block of memory are touched after being allocated.}
  201. {*******************************************************************************)
  202.  
  203.     function NewPtrMargin (requestedSize: Size; appHeapAlloc: Boolean; clearMem: Boolean): Ptr;
  204.  
  205.  
  206. implementation
  207.  
  208. (*******************************************************************************}
  209. {* Constants}
  210. {*******************************************************************************)
  211.  
  212.     const
  213.         kEmergMemSize = 32768; {Number of bytes of emergency memory to allocate}
  214.         kMemoryMargin = 32768; {Minimum amount of free memory I allow in the heap}
  215.  
  216.  
  217. (*******************************************************************************}
  218. {* Global Variables}
  219. {*******************************************************************************)
  220.  
  221.     var
  222.         gEmergMem: Handle; {Handle to block of emergency memory}
  223.  
  224.  
  225. {$S Main}
  226. (*******************************************************************************}
  227. {* Private: AppGrowZone - Custom grow-zone procedure}
  228. {*}
  229. {* This is a very basic grow zone procedure.  My application keeps a reserve}
  230. {* handle of memory in case the Memory Manager gets a request for some memory}
  231. {* that is not available in my heap.  If memory were to get tight (<32k), the}
  232. {* Toolbox will crash the system.  This grow-zone proc tries to thwart that}
  233. {* possibility by releasing the 32K block of emergency memory if it hasn’t been}
  234. {* released already and if the amount of memory requested is less than 32K.}
  235. {* Hopefully, that’s enough to satisfy the memory request.}
  236. {*}
  237. {* There are three conditions in which the emergency memory isn’t freed.  If the}
  238. {* emergency memory is already free, obviously there isn’t much that can be done.}
  239. {* If the emergency memory is equal to GZSaveHnd, then it was the reallocation of}
  240. {* emergency memory that caused this grow-zone proc to be called.  So it doesn’t}
  241. {* make much sense to free it in that case.  If the size of the memory request is}
  242. {* more than the size of emergency memory, then I don’t bother to free emergency}
  243. {* memory because I assume that the toolbox handles such huge requests for memory}
  244. {* properly.  Warning: that isn’t always a good assumption, but that’s not my}
  245. {* fault.}
  246. {*}
  247. {*     WARNING: Register A5 might not be valid when grow-zone procedures}
  248. {*     are called. Read Technical Note #136 and 208.}
  249. {*}
  250. {* The "cbNeeded" parameter is the number of bytes that the Memory Manager needs}
  251. {* to fulfill the memory request it had received.  The number of bytes actually}
  252. {* freed by AppGrowZone is returned.}
  253. {*******************************************************************************)
  254.  
  255.  
  256.     function AppGrowZone (cbNeeded: Size): LongInt;
  257.  
  258.         var
  259.             theA5: LongInt; {Value of A5 when AppGrowZone is called}
  260.  
  261.     begin
  262.         (* Remember the current value of A5 *)
  263.         theA5 := SetCurrentA5;
  264.  
  265.         (* Free emergency memory if possible *)
  266.         if (gEmergMem^ <> nil) & (gEmergMem <> GZSaveHnd) & (cbNeeded <= kEmergMemSize) then
  267.             begin
  268.                 EmptyHandle(gEmergMem);
  269.                 AppGrowZone := kEmergMemSize
  270.             end
  271.         else
  272.             AppGrowZone := 0;
  273.  
  274.         (* Restore A5 *)
  275.         theA5 := SetA5(theA5)
  276.     end;
  277.  
  278.  
  279. {$S Main}
  280. (*******************************************************************************}
  281. {* Public: ConnectAppGZ}
  282. {*}
  283. {* It’s pretty self-explanatory.}
  284. {*******************************************************************************)
  285.  
  286.     procedure ConnectAppGZ;
  287.  
  288.     begin
  289.         SetGrowZone(@AppGrowZone)
  290.     end;
  291.  
  292.  
  293. {$S Main}
  294. (*******************************************************************************}
  295. {* Public: DisconnectAppGZ}
  296. {*}
  297. {* It’s pretty self-explanatory.}
  298. {*******************************************************************************)
  299.  
  300.     procedure DisconnectAppGZ;
  301.  
  302.     begin
  303.         SetGrowZone(nil)
  304.     end;
  305.  
  306.  
  307. {$S %A5Init}
  308. (*******************************************************************************}
  309. {* Public: InitEmergMem}
  310. {*}
  311. {* It’s pretty self-explanatory.}
  312. {*******************************************************************************)
  313.  
  314.     procedure InitEmergMem;
  315.  
  316.     begin
  317.         gEmergMem := NewHandle(kEmergMemSize);
  318.         ConnectAppGZ
  319.     end;
  320.  
  321.  
  322. {$S Main}
  323. (*******************************************************************************}
  324. {* Public: NoEmergMem}
  325. {*}
  326. {* We check on the handle and the master pointer of gEmergMem to see if the}
  327. {* emergency memory block has been emptied by AppGrowZone, or was never allocated}
  328. {* in the first place.}
  329. {*******************************************************************************)
  330.  
  331.     function NoEmergMem: Boolean;
  332.  
  333.     begin
  334.         (* Empty handle means no emergency memory *)
  335.         NoEmergMem := (gEmergMem = nil) | (gEmergMem^ = nil)
  336.     end;
  337.  
  338.  
  339. {$S Main}
  340. (*******************************************************************************}
  341. {* Public: RecoverEmergMem}
  342. {*}
  343. {* Not much to describe.}
  344. {*******************************************************************************)
  345.  
  346.     procedure RecoverEmergMem;
  347.  
  348.     begin
  349.         ReallocHandle(gEmergMem, kEmergMemSize);
  350.     end;
  351.  
  352.  
  353. {$S Main}
  354. (*******************************************************************************}
  355. {* Public: FailLowMemory}
  356. {*}
  357. {* PurgeSpace is used to determine how much free memory there’d be in the heap if}
  358. {* all purgeable blocks were purged.  If this amount is less than the amount}
  359. {* needed, or if there isn’t any emergency memory, TRUE is returned.}
  360. {*******************************************************************************)
  361.  
  362.     function FailLowMemory (memRequest: LongInt): Boolean;
  363.  
  364.         var
  365.             total: LongInt; {Total amount of free memory if heap was purged}
  366.             contig: LongInt; {Max amount of free contiguous memory if heap was purged}
  367.  
  368.     begin
  369.         PurgeSpace(total, contig);(*<*)
  370.  (*<*)
  371.         FailLowMemory := (total < (memRequest + kMemoryMargin)) | NoEmergMem
  372.     end;
  373.  
  374.  
  375. {$S Main}
  376. (*******************************************************************************}
  377. {* Public: NewHandleMargin}
  378. {*}
  379. {* I don’t call SysError with an ID 25 if there isn’t enough memory to satisfy}
  380. {* the request, so there isn’t much reason to use the grow-zone proc.  So, I}
  381. {* disconnect the grow-zone proc temporarily just before I allocate the memory.}
  382. {*******************************************************************************)
  383.  
  384.     function NewHandleMargin (requestedSize: Size; appHeapAlloc: Boolean; clearMem: Boolean): Handle;
  385.  
  386.         var
  387.             total: LongInt; {Total free bytes after a theoretical heap purge/compaction}
  388.             contig: LongInt; {Largest contig free block from theoretical heap purge/compaction}
  389.  
  390.     begin
  391.         if FailLowMemory(requestedSize) then
  392.             NewHandleMargin := nil
  393.         else
  394.             begin
  395.                 (* We handle memFullErr properly, so don’t need grow-zone proc *)
  396.                 DisconnectAppGZ;
  397.  
  398.                 (* Allocate the memory with the requested options *)
  399.                 if (not appHeapAlloc) and clearMem then
  400.                     NewHandleMargin := NewHandleSysClear(requestedSize)
  401.                 else if (not appHeapAlloc) then
  402.                     NewHandleMargin := NewHandleSys(requestedSize)
  403.                 else if clearMem then
  404.                     NewHandleMargin := NewHandleClear(requestedSize)
  405.                 else
  406.                     NewHandleMargin := NewHandle(requestedSize);
  407.  
  408.                 (* Connect up the grow-zone proc again *)
  409.                 ConnectAppGZ
  410.             end
  411.     end;
  412.  
  413.  
  414. {$S Main}
  415. (*******************************************************************************}
  416. {* Public: NewPtrMargin}
  417. {*}
  418. {* I don’t call SysError with an ID 25 if there isn’t enough memory to satisfy}
  419. {* the request, so there isn’t much reason to use the grow-zone proc.  So, I}
  420. {* disconnect the grow-zone proc temporarily just before I allocate the memory.}
  421. {*******************************************************************************)
  422.  
  423.     function NewPtrMargin (requestedSize: Size; appHeapAlloc: Boolean; clearMem: Boolean): Ptr;
  424.  
  425.         var
  426.             total: LongInt; {Total free bytes after a theoretical heap purge/compaction}
  427.             contig: LongInt; {Largest contig free block from theoretical heap purge/compaction}
  428.  
  429.     begin
  430.         if FailLowMemory(requestedSize) then
  431.             NewPtrMargin := nil
  432.         else
  433.             begin
  434.                 (* We handle memFullErr properly, so don’t need grow-zone proc *)
  435.                 DisconnectAppGZ;
  436.  
  437.                 (* Allocate the memory with the requested options *)
  438.                 if (not appHeapAlloc) and clearMem then
  439.                     NewPtrMargin := NewPtrSysClear(requestedSize)
  440.                 else if not appHeapAlloc then
  441.                     NewPtrMargin := NewPtrSys(requestedSize)
  442.                 else if clearMem then
  443.                     NewPtrMargin := NewPtrClear(requestedSize)
  444.                 else
  445.                     NewPtrMargin := NewPtr(requestedSize);
  446.  
  447.                 (* Connect up the grow-zone proc again *)
  448.                 ConnectAppGZ
  449.             end
  450.     end;
  451.  
  452. end.